package com.nulldev.util.web.HttpServer3.extensions.flexiProxy;

import java.util.Map;

import com.nulldev.util.data.Arrays.maps.FastMaps;
import com.nulldev.util.web.HttpServer3.Response;
import com.nulldev.util.web.HttpServer3.extensions.flexiProxy.internal.FlexiProxyInternalDebugHandler;
import com.nulldev.util.web.HttpServer3.extensions.flexiProxy.internal.FlexiProxyInternalDebugRule;
import com.nulldev.util.web.HttpServer3.pages.v3.HttpPage3;
import com.nulldev.util.web.URLUtils.URLQueryParser;
import com.nulldev.util.web.URLUtils.URLQueryParser.URLQueryParserOptions;
import com.nulldev.util.web.URLUtils.URLUtils;

/**
 * WORK IN PROGRESS. <br>
 * <b>NOT READY FOR PRODUCTION USE.</b> <br>
 * 
 */
public class FlexiProxyPage extends HttpPage3 {

	private static final String FLEXI_PROXY_DOMAIN_INTERNAL = "_fpi_domain";
	private static final String FLEXI_PROXY_DEBUG_PAGE = "flexiproxy-debug";

	/* */

	private final boolean blockedNonRuledPages;
	private final Map<String, FlexiProxyRule> rules;
	private final Map<String, FlexiProxyHandler> handlers;

	private void __inject_debugging(final Map<String, FlexiProxyRule> rules, final Map<String, FlexiProxyHandler> handlers) {
		rules.put(FLEXI_PROXY_DEBUG_PAGE, new FlexiProxyInternalDebugRule());
		handlers.put(FLEXI_PROXY_DEBUG_PAGE, new FlexiProxyInternalDebugHandler());
	}

	private void __validate_handlers(final Map<String, FlexiProxyRule> rules, final Map<String, FlexiProxyHandler> handlers) {
		for (final Map.Entry<String, FlexiProxyHandler> handler : handlers.entrySet()) {
			if (!rules.containsKey(handler.getKey())) {
				throw new RuntimeException("__validate_handlers: " + handler.getKey() + " is missing a rule!");
			}
		}
	}

	protected FlexiProxyPage(final String page, final Map<String, FlexiProxyRule> rules, final Map<String, FlexiProxyHandler> handlers,
			final boolean blockNonRuledPages) {
		super(page);
		__inject_debugging(rules, handlers);
		__validate_handlers(rules, handlers);
		this.rules = rules;
		this.handlers = handlers;
		this.blockedNonRuledPages = blockNonRuledPages;
	}

	@SuppressWarnings("resource")
	@Override
	public Response onRequest(final Request request) {
		final Map<String, String> s = URLQueryParser.extract(request.url(), URLQueryParserOptions.PREFER_EXPERIMENTAL,
				(URLUtils.USE_URL_ACCEL_V2 ? null : URLQueryParserOptions.DISABLE_URL_ACCEL), URLQueryParserOptions.DISABLE_DECODING,
				URLQueryParserOptions.TREAT_URL_AS_QUERY);
		if (!s.containsKey(FLEXI_PROXY_DOMAIN_INTERNAL))
			return new Response(400).header("X-Served-By", "FLEXI_PROXY");
		final String host = s.get(FLEXI_PROXY_DOMAIN_INTERNAL);
		final Map<String, String> headers = FastMaps.map(s.size());
		headers.putAll(s);
		headers.remove(FLEXI_PROXY_DOMAIN_INTERNAL);
		if (this.blockedNonRuledPages && !this.rules.containsKey(host))
			return new Response(403).header("X-Served-By", "FLEXI_PROXY");
		boolean blocked = false;
		__rule_processing: {
			for (final Map.Entry<String, FlexiProxyRule> rules : this.rules.entrySet()) {
				if (!rules.getKey().equalsIgnoreCase(host))
					continue;
				switch (rules.getValue().handle(host, headers)) {
					case FLEXI_PROXY_ALLOW:
						continue;
					case FLEXI_PROXY_BLOCK:
						blocked = true;
						break __rule_processing;
					case FLEXI_PROXY_REDIRECT_TO_CUSTOM_HANDLER:
						/* TODO */
						break;
					default:
						throw new UnsupportedOperationException("Unhandled rule type!");
				}
			}
		}
		if (blocked) {
			return new Response(403).header("X-Served-By", "FLEXI_PROXY");
		}
		return null;
	}
}
